#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <string>
#include <vector>
#include <sys/time.h>
#include <getopt.h>

// #include "Lexer.h"
// #include "SQLParser.h"
// #include "SQLExprParser.h"
// #include "SQLSelectParser.h"
// #include "SQLStatementParser.h"
// #include "SQLDDLParser.h"
// #include "SQLCreateTableParser.h"
// #include "OdpsSelectParser.h"

#include "../utils/log.h"
// #include <gflags/gflags.h>

#include "../sql/parser/Token.h"
#include "../sql/SQLUtils.h"
#include "../sql/ast/expr/SQLAllColumnExpr.h"
#include "../sql/ast/expr/SQLBinaryOpExpr.h"
#include "../sql/ast/expr/SQLCharExpr.h"
#include "../sql/ast/expr/SQLInListExpr.h"
#include "../sql/ast/statement/SQLSelectStatement.h"
#include "../sql/ast/statement/SQLInsertStatement.h"
#include "../sql/ast/statement/SQLDeleteStatement.h"
#include "../sql/ast/statement/SQLDropTableStatement.h"
#include "../sql/ast/statement/SQLTruncateStatement.h"
#include "../sql/ast/statement/SQLUpdateStatement.h"
#include "../sql/ast/statement/SQLRevokeStatement.h"
#include "../sql/ast/statement/SQLSubqueryTableSource.h"
#include "../sql/ast/statement/SQLJoinTableSource.h"
#include "../sql/ast/statement/SQLSelectItem.h"
#include "../sql/ast/SQLStatement.h"

#include "../utils/instanceof.h"
#include "../dialect/mysql/visitor/MySqlASTVisitorAdapter.h"

// DEFINE_string(dbtype, "mysql", "dbtype");
// DEFINE_string(sql, "errorsql", "sql");

static inline double millitime()
{
  struct timeval now;
  gettimeofday(&now, NULL);
  double ret = now.tv_sec + now.tv_usec / 1000.0 / 1000.0;
  return ret;
}

void testSQLUtils(string_ptr x1, string_ptr dbtype)
{
#ifdef TRY
  try
  {
#endif
    LOG_INFO << "dbtype:" << dbtype->c_str() << ",sql:" << x1->c_str();
    DbType_ptr pdbtype = DbType::valueOf(dbtype);
    SQLStatement_list_ptr stmt = SQLUtils::parseStatements(x1, pdbtype);
    LOG_INFO << ": stmt.size:" << stmt->size();
    for (auto it : *stmt)
    {
      if (instanceof <SQLStatement, SQLSelectStatement>(it))
      {
        std::shared_ptr<SQLSelectStatement> sqlSelectStatement = std::dynamic_pointer_cast<SQLSelectStatement>(it);
        SQLSelect_ptr sqlSelect = sqlSelectStatement->getSelect();
        if (sqlSelect == nullptr)
        {
          continue;
        }
        std::shared_ptr<SQLSelectQuery> sqlSelectQuery = sqlSelect->getQuery();
        if (sqlSelectQuery == nullptr)
        {
          continue;
        }
        std::shared_ptr<SQLSelectQueryBlock> sqlSelectQueryBlock = std::dynamic_pointer_cast<SQLSelectQueryBlock>(sqlSelectQuery);
        // sqlSelectQueryBlock->getSelectList();
        std::shared_ptr<SQLTableSource> sqlTableSource = sqlSelectQueryBlock->getFrom();
        LOG_INFO << ": dbType:" << sqlSelectQueryBlock->dbType->name->c_str();
        if (instanceof <SQLTableSource, SQLExprTableSource>(sqlTableSource))
        {
          std::shared_ptr<SQLExprTableSource> sqlExprTableSource = std::dynamic_pointer_cast<SQLExprTableSource>(sqlTableSource);
          if (sqlExprTableSource != nullptr)
          {
            string_ptr sourceAlias = sqlExprTableSource->getAlias();
            if (sourceAlias != nullptr)
            {
              LOG_INFO << ": sourceAlias:" << sourceAlias->c_str();
            }
            string_ptr schemaName = sqlExprTableSource->getSchema();
            if (schemaName != nullptr)
            {
              LOG_INFO << ": schemaName:" << schemaName->c_str();
            }
            string_ptr tableName = sqlExprTableSource->getTableName();
            if (tableName != nullptr)
            {
              LOG_INFO << ": tableName:" << tableName->c_str();
            }
          }
          SQLSelectItem_list_ptr sqlSelectItems = sqlSelectQueryBlock->getSelectList();

          LOG_INFO << ": sqlSelectItems.size:" << sqlSelectItems->size();

          for (auto item : *sqlSelectItems)
          {
            SQLExpr_ptr sqlExpr = item->getExpr();
            if (instanceof <SQLExpr, SQLAllColumnExpr>(sqlExpr))
            {
              LOG_INFO << ": SELECT后跟的是\"*\"列";
              LOG_INFO << ": 解析时如果列信息没有添加过来会完全解析不到schema和table，此时返回一组列为*号的解析结果，用于数据集匹配";
            }
            else if (instanceof <SQLExpr, SQLIdentifierExpr>(sqlExpr))
            {
              // SELECT后跟的是最简列名，如MENUTARGET
              SQLIdentifierExpr_ptr sqlIdentifierExpr = std::dynamic_pointer_cast<SQLIdentifierExpr>(sqlExpr);
              string_ptr curColumnName = sqlIdentifierExpr->getName();

              if (curColumnName != nullptr)
              {
                LOG_INFO << ": curColumnName:" << curColumnName->c_str();
              }
            }
            else if (instanceof <SQLExpr, SQLPropertyExpr>(sqlExpr))
            {
              // SELECT后跟的是带别名的列名，如T0.MENUTARGET
              SQLPropertyExpr_ptr sqlPropertyExpr = std::dynamic_pointer_cast<SQLPropertyExpr>(sqlExpr);
              string_ptr sourceName = sqlPropertyExpr->getOwnerName();
              string_ptr curColumnName = sqlPropertyExpr->getName();

              LOG_INFO << ": sourceName:" << sourceName;
              LOG_INFO << ": curColumnName:" << curColumnName;
            }
            else
            {
              LOG_INFO << ": else";
            }
          }

          SQLExpr_ptr sqlWhere = sqlSelectQueryBlock->getWhere();
          if (instanceof <SQLExpr, SQLInListExpr>(sqlWhere))
          {
            // SQLInListExpr 指 run_id in ('1', '2') 这一情况
            SQLInListExpr_ptr inListExpr = std::dynamic_pointer_cast<SQLInListExpr>(sqlWhere);
            SQLExpr_list_ptr valueExprs = inListExpr->getTargetList();
            for (SQLExpr_ptr expr : *valueExprs)
            {
              LOG_INFO << ":" << expr;
            }
          }
          else
          {
            // SQLBinaryOpExpr 指 run_id = '1' 这一情况
            SQLBinaryOpExpr_ptr binaryOpExpr = std::dynamic_pointer_cast<SQLBinaryOpExpr>(sqlWhere);
            string_ptr op_name = (binaryOpExpr->getOperator())->getName();
            if (op_name != nullptr)
            {
              LOG_INFO << ": op name:" << op_name->c_str();
            }
            SQLIdentifierExpr_ptr left = std::dynamic_pointer_cast<SQLIdentifierExpr>(binaryOpExpr->getLeft());
            SQLIntegerExpr_ptr right = std::dynamic_pointer_cast<SQLIntegerExpr>(binaryOpExpr->getRight());
            string_ptr left_name = left->getName();
            if (left_name != nullptr)
            {
              LOG_INFO << left_name->c_str() << " --> " << right->intValue();
            }
          }

          SQLOrderBy_ptr sqlOrderBy = sqlSelectQueryBlock->getOrderBy();
          if (sqlOrderBy != nullptr)
          {
            auto sqlOrderByAttr = sqlOrderBy->getAttributes();
            LOG_INFO << ": sqlOrderByAttr.size:" << sqlOrderByAttr->size();
            for (auto pair : *sqlOrderByAttr)
            {
              std::string first = pair.first;
              // if (first != nullptr)
              // {
                LOG_INFO << "Key: " << first.c_str() << ", Value: " << pair.second;
              // }
            }

            // LOG_INFO << ":" << sqlOrderBy->lexer->stringVal();

            SQLSelectOrderByItem_list_ptr sqlOrderByItem = sqlOrderBy->getItems();
            LOG_INFO << "sqlOrderByItem.size:" << sqlOrderByItem->size();
            for (auto pair : *sqlOrderByItem)
            {
              auto expr = pair->getExpr();
              if (instanceof <SQLExpr, SQLIdentifierExpr>(expr))
              {
                string_ptr name = std::dynamic_pointer_cast<SQLIdentifierExpr>(expr)->getName();
                if (name != nullptr)
                {
                  LOG_INFO << name->c_str();
                }
              }
              // LOG_INFO << pair->getHint()->getText();
              // if (pair->getType() != NULL) {
              //   LOG_INFO << ":" << pair->getType();
              // }
              // pair->getExpr()->toString();
            }
          }
        }
        else if (instanceof <SQLTableSource, SQLSubqueryTableSource>(sqlTableSource))
        {
          LOG_INFO << ":";
        }
        else if (instanceof <SQLTableSource, SQLJoinTableSource>(sqlTableSource))
        {
          LOG_INFO << ":";
        }
        else
        {
          LOG_INFO << ": else";
        }
      }
      else if (instanceof <SQLStatement, SQLDropTableStatement>(it))
      {
        SQLDropTableStatement_ptr sqlDropTableStatement = std::dynamic_pointer_cast<SQLDropTableStatement>(it);
        std::shared_ptr<std::list<std::shared_ptr<SQLExprTableSource>>> tables = sqlDropTableStatement->getTableSources();
        for (std::shared_ptr<SQLExprTableSource> table : *tables)
        {
          string_ptr schemaName = table->getSchema();
          if (schemaName != nullptr)
          {
            LOG_INFO << "schemaName:" << schemaName->c_str();
          }
          string_ptr tableName = table->getTableName();
          if (tableName != nullptr)
          {
            LOG_INFO << "tableName:" << tableName->c_str();
          }
        }
      }
      else if (instanceof <SQLStatement, SQLTruncateStatement>(it))
      {
        SQLTruncateStatement_ptr sqlTruncateStatement = std::dynamic_pointer_cast<SQLTruncateStatement>(it);
        SQLExprTableSource_list_ptr tables = sqlTruncateStatement->getTableSources();
        LOG_INFO << "tables size:" << tables->size();
        for (auto table : *tables)
        {
          string_ptr schemaName = table->getSchema();
          if (schemaName != nullptr)
          {
            LOG_INFO << "schemaName:" << schemaName->c_str();
          }
          string_ptr tableName = table->getTableName();
          if (tableName != nullptr)
          {
            LOG_INFO << "tableName:" << tableName->c_str();
          }
        }
      }
      else if (instanceof <SQLStatement, SQLUpdateStatement>(*it))
      {
      }
      else if (instanceof <SQLStatement, SQLDeleteStatement>(*it))
      {
      }
      else if (instanceof <SQLStatement, SQLInsertStatement>(*it))
      {
        SQLInsertStatement_ptr sqlInsertStatement = std::dynamic_pointer_cast<SQLInsertStatement>(it);
        SQLName_ptr tableName = sqlInsertStatement->getTableName();
        if (instanceof <SQLName, SQLIdentifierExpr>(tableName))
        {
          LOG_INFO << "table name:" << *std::dynamic_pointer_cast<SQLIdentifierExpr>(tableName)->getName();
        }
        SQLExpr_list_ptr sqlExpr = sqlInsertStatement->getColumns();
        for (auto sql_expr : *sqlExpr)
        {
          if (instanceof <SQLExpr, SQLIdentifierExpr>(sql_expr))
          {
            LOG_INFO << "column name:" << *std::dynamic_pointer_cast<SQLIdentifierExpr>(sql_expr)->getName();
          }
        }

        std::shared_ptr<std::list<std::shared_ptr<SQLInsertStatement_ValuesClause>>> valuesList = sqlInsertStatement->getValuesList();
        for (const auto pair : *valuesList)
        {
          SQLExpr_list_ptr values = pair->getValues();
          for (SQLExpr_ptr expr : *values)
          {
            // LOG_INFO << ":" << expr;
            if (instanceof <SQLExpr, SQLCharExpr>(expr))
            {
              LOG_INFO << "getText:" << *std::dynamic_pointer_cast<SQLCharExpr>(expr)->getText();
            }
          }
        }
      }
      else if (instanceof <SQLStatement, SQLRevokeStatement>(*it))
      {
        LOG_INFO << "SQLRevokeStatement";
        std::shared_ptr<SQLRevokeStatement> sqlRevokeStatement = std::dynamic_pointer_cast<SQLRevokeStatement>(it);
        if (sqlRevokeStatement == nullptr)
        {
          LOG_INFO << "sqlRevokeStatement == nullptr";
          continue;
        }
       
        // sqlRevokeStatement->accept(MySqlASTVisitorAdapter_ptr(new MySqlASTVisitorAdapter()));
      }
      else
      {
        LOG_INFO << ": else";
      }
    }
#ifdef TRY
  }
  catch (const std::exception &e)
  {
    LOG_INFO << "catch:" << e.what();
  }
#endif
  LOG_INFO << "parser sql:" << x1->c_str();
}

int main(int argc, char **argv)
{
  // setlocale(LC_CTYPE, "");
  // 设置本地化以支持宽字符输出
  std::locale::global(std::locale(""));
  // gflags::ParseCommandLineFlags(&argc, &argv, true);
  // FLAGS_alsologtostderr = 1;
  // google::InitGoogleLogging(argv[0]);
  // FLAGS_colorlogtostderr = true; // 设置输出颜色
  std::string sql;
  std::string dbtype;

  int opt, lopt;
  int digit_optind = 0;
  int option_index = 0;
  const char *optstring = "hvt:s:";
  static struct option long_options[] = {
      {"help", no_argument, &lopt, 'h'},
      {"version", no_argument, &lopt, 'v'},
      {"dbtype", required_argument, &lopt, 't'},
      {"sql", required_argument, &lopt, 's'},
      {0, 0, 0, 0}};

  while ((opt = getopt_long_only(argc, argv, optstring, long_options, &option_index)) != -1)
  {
    printf("opt = %c\t\t", opt);
    printf("optarg = %s\t\t", optarg);
    printf("optind = %d\t\t", optind);
    printf("argv[optind] =%s\t\t", argv[optind]);
    printf("option_index = %d\n", option_index);
    if (opt == 0)
      opt = lopt;
    switch (opt)
    {
    case 'a':
      printf("opt a==%c\n", opt);
      break;
    case 'b':
      printf("opt b==%c, arg: %s\n", opt, optarg);
      break;
    case 'c':
      printf("opt c==%c, arg: %s\n", opt, optarg);
      break;
    case 'd':
      printf("opt d==%c, arg: %s\n", opt, optarg);
      break;
    case 'h':
      printf("opt help==%d\n", opt);
      printf("./bench --dbtype=mysql --sql=\"\"");
      break;
    case 'v':
      printf("opt version==%d\n", opt);
      break;
    case 3:
      printf("opt infile==%d arg: %s\n", opt, optarg);
      break;
    case 't':
      LOG_INFO << "opt dbtype==" << opt << " arg: " << optarg;
      dbtype = optarg;
      break;
    case 's':
      LOG_INFO << "opt sql==" << opt << " arg: " << optarg;
      sql = optarg;
      break;
    default:
      printf("error opt %c");
      return -1;
    }
  }

  string_ptr str = make_string_ptr(sql);
  size_t pos = 0;
  while ((pos = str->find("\\n", pos)) != std::string ::npos)
  {
    str->replace(pos, 2, "\n");
    pos += 2; // 跳过已替换的换行符和下一个字符
  }

#ifdef TRY
  try
  {
#endif
    testSQLUtils(str, make_string_ptr(dbtype));
#ifdef TRY
  }
  catch (const std::exception &e)
  {
    LOG_INFO << "catch:" << e.what();
  }
#endif
  return 0;
}
